using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using GA;

namespace _queensGA
{
    public partial class frmBoard : Form
    {
        int boardLength;
        int tileSize = 25;

        private string boardId;

        public string BoardId
        {
            get
            {
                if (boardId == null || boardId.Length == 0)
                    boardId = Guid.NewGuid().ToString();
                return boardId;
            }            
        }

        public delegate void UpdateStateHandler(frmBoard board, Population p);
        public event UpdateStateHandler UpdateState;
        public delegate void EvolutionPausedHandler(frmBoard board, bool isPaused);
        public event EvolutionPausedHandler EvolutionPaused;

        public delegate void EvolutionCanceledHandler(frmBoard board);
        public event EvolutionCanceledHandler EvolutionCanceled;

        public void OnUpdateState()
        {
            if (UpdateState != null)
            {
                UpdateState(this, pop);
            }
        }

        public void OnEvolutionPaused(bool isPaused)
        {
            if (EvolutionPaused != null)
            {
                EvolutionPaused(this, isPaused);
            }
        }

        public void OnEvolutionCanceled()
        {
            if (EvolutionCanceled != null)
                EvolutionCanceled(this);
        }

        public bool done = false;
        public bool reqExit = false;
        public bool reqPause = false;


        public double mutationRate;
        public double crossOverRate;
        public double elitism;
        public int elitismDelayEpochs = 50;

        private Population pop;
        private List<int> state = new List<int>();
        private Thread tWorker;

        

        public frmBoard()
        {            
            InitializeComponent();
        }

        public frmBoard(int n, int popSize, double mutationRate, double crossOverRate, double elitism)
        {
            InitializeComponent();
            //CheckForIllegalCrossThreadCalls = false; // I know it is bad.
            boardLength = n;

            this.mutationRate = mutationRate;
            this.crossOverRate = crossOverRate;
            this.elitism = elitism;

            lblBoardLength.Text = boardLength.ToString();
            lblPopulation.Text = popSize.ToString();
            lblMutation.Text = mutationRate.ToString("N2");
            lblCrossOver.Text = crossOverRate.ToString("N2");
            lblElitism.Text = elitism.ToString("N2");

            pop = new Population(popSize, boardLength, mutationRate, crossOverRate, elitism, elitismDelayEpochs);

            SetupBoard();            
        }

        public void Start()
        {
            tWorker = new Thread(new ThreadStart(Evolve));
            tWorker.Start();
        }

        private void SetupBoard()
        {
            cbChessBoard.SetupBoard(boardLength, tileSize);
            this.Width = cbChessBoard.Width + 2 * tileSize;
            this.Height = cbChessBoard.Height + 4 * tileSize;
            cbChessBoard.Left = (this.Width - cbChessBoard.Width - tileSize / 2) / 2;
            cbChessBoard.Top = (this.Height - cbChessBoard.Height + tileSize) / 2;
        }

        public bool IsAlive()
        {
            return tWorker.IsAlive;
        }

        public string GetInfo()
        {
            return pop.ToOneLineString();
        }

        void Evolve()
        {
            int bestFit = int.MaxValue;
            int decimate = 0;
            while (!reqExit)
            {
                pop.NextGeneration();

                decimate++;
                if (decimate == 9)
                {
                    decimate = 0;
                    SafeSetText(lblGeneration, pop.generation.ToString());
                }
                
                if (bestFit == 0)
                    break;

                if (bestFit > pop.bestSoFar.Fitness)
                {
                    bestFit = pop.bestSoFar.Fitness;
                    UpdateFormState(pop.bestSoFar);
                    SafeSetText(lblGeneration, pop.generation.ToString());
                    OnUpdateState();
                }
            }

            OnUpdateState();

            done = true;
            UpdateFormState(pop.bestSoFar);
        }

        private void SafeSetText(Control c, string text)
        {
            if (c.InvokeRequired)
            {
                c.Invoke((MethodInvoker)delegate { SafeSetText(c, text); });
            }
            else
            {
                c.Text = text;
            }
        }

        public void UpdateFormState(Chromosome c)
        {
            if (boardLength != c.genes.Count)
                return;
            if (this.InvokeRequired)
            {
                this.Invoke((MethodInvoker)
                    delegate { UpdateFormState(c); });
            }
            else
            {
                state = new List<int>(c.genes.ToArray());
                cbChessBoard.SetState(state, done);
                lblCost.Text = c.Fitness.ToString();
                this.Text = "Best: " + c.ToString();
                if (done)
                {
                    lblCost.ForeColor = Color.Blue;
                    this.Text = "A solution: " + c.ToString();
                }
                cbChessBoard.Invalidate(true);
            }
        }

       

        private void frmBoard_KeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = true;
        }

        private void btnToggle_Click(object sender, EventArgs e)
        {
            if (reqPause && tWorker.ThreadState == ThreadState.Suspended)
            {
                tWorker.Resume();                
                btnToggle.Text = "Pause";
                OnEvolutionPaused(false);
            }
            else if (!reqPause && tWorker.ThreadState == ThreadState.Running)
            {
                tWorker.Suspend();
                btnToggle.Text = "Resume";
                OnEvolutionPaused(true);
            }
            reqPause = !reqPause;
        }

        private void frmBoard_FormClosing(object sender, FormClosingEventArgs e)
        {
            reqExit = true;

            if (tWorker.ThreadState == ThreadState.Suspended)
            {
                tWorker.Resume();
                tWorker.Abort();
            }
            else if (tWorker.IsAlive)
            {
                tWorker.Abort();                
            }
            
            if (!done)
                OnEvolutionCanceled();            
        }

        private void frmBoard_Load(object sender, EventArgs e)
        {

        }       
    }
}